#include "fyp/shadows/VarPCF2.h"

//------------------------------------------------------------------------------
// additional header includes
#include "fyp/ObjectManager.h"
#include "fyp/ResourcePool.h"

#include "fyp/render/Renderer.h"

using namespace ming::fyp::render;

//------------------------------------------------------------------------------
namespace ming
{
namespace fyp
{
namespace shadows
{
	//--------------------------------------------------------------------------
	TVarPCF2::TVarPCF2()
	{
		mPendingShadowMapSize = 512;
	}
	
	//--------------------------------------------------------------------------
	TVarPCF2::~TVarPCF2()
	{
	}

	//--------------------------------------------------------------------------
	void TVarPCF2::Init(TResourcePool *resPool, TGuiManager *guiMgr)
	{
		resPool->Add<IShader>("VarPCF2.Apply", make_tuple( STR("../media/shader/VarPCF2.Apply.fx"), STR("Apply") ) );
		resPool->Add<IShader>("VarPCF2.Generate", make_tuple( STR("../media/shader/VarPCF2.Generate.fx"), STR("Generate") ) );
		resPool->Add<IShader>("VarPCF2.DownSample", make_tuple( STR("../media/shader/VarPCF2.DownSample.fx"), STR("DownSample") ) );
	}

	//--------------------------------------------------------------------------
	void TVarPCF2::RenderScene(
		render::IRenderer *renderer,
		TResourcePool *resPool,
		TObjectManager *objMgr)
	{

		if(mPendingShadowMapSize > 0)
		{
			RemoveShadowMaps(resPool);
			CreateShadowMaps(resPool);
			mPendingShadowMapSize = -1;
		}

		GenerateShadowMap(renderer, resPool, objMgr);

		RenderSceneWithShadowMap(renderer, resPool, objMgr);
	}
	
	//--------------------------------------------------------------------------
	void TVarPCF2::Final(TResourcePool *resPool)
	{
		resPool->Remove<IShader>("VarPCF2.Apply");
		resPool->Remove<IShader>("VarPCF2.Generate");
		resPool->Remove<IShader>("VarPCF2.DownSample");

		RemoveShadowMaps(resPool);
	}

	//--------------------------------------------------------------------------
	void TVarPCF2::ChangeShadowMapSize(size_t sz)
	{
		mPendingShadowMapSize = (int)sz;
	}

	//--------------------------------------------------------------------------
	const char* TVarPCF2::GetDescription()
	{
		return "Varied Percentage Closer Filtering with Simplex Dilation";
	}
	
	//--------------------------------------------------------------------------
	void TVarPCF2::CreateShadowMaps(TResourcePool *resPool)
	{
		CreateShadowMap(resPool, "VarPCF2.SMap", IRenderedTexture::FLOAT16_GR, mPendingShadowMapSize);
		CreateShadowMap(resPool, "VarPCF2.SMap.1over4", IRenderedTexture::FLOAT16_GR, mPendingShadowMapSize / 4);
	}

	//--------------------------------------------------------------------------
	void TVarPCF2::RemoveShadowMaps(TResourcePool *resPool)
	{
		resPool->Remove<IRenderedTexture>("VarPCF2.SMap");
		resPool->Remove<IRenderedTexture>("VarPCF2.SMap.1over4");
	}

	//--------------------------------------------------------------------------
	void TVarPCF2::CreateShadowMap(
		TResourcePool *resPool,
		const TStr &name,
		size_t format,
		size_t size)
	{
		resPool->Add<IRenderedTexture>(
			name, 
			make_tuple( format,
						size,
						size,
						true) );

		resPool->Get<IRenderedTexture>(name)->SetFilterMode(
			ITexture::POINT, ITexture::POINT, ITexture::POINT);

		resPool->Get<IRenderedTexture>(name)->SetAddressMode(
			ITexture::CLAMP, ITexture::CLAMP, ITexture::CLAMP);
	}

	//--------------------------------------------------------------------------
	void TVarPCF2::GenerateShadowMap(
		render::IRenderer *renderer,
		TResourcePool *resPool,
		TObjectManager *objMgr)
	{
		IShader *shader = resPool->Get<IShader>("VarPCF2.Generate");

		renderer->BindRenderTarget(resPool->Get<IRenderedTexture>("VarPCF2.SMap"), NULL, NULL, NULL, true);
		renderer->ClearColor( TVec4(1.0f, 1.0f, 1.0f, 1.0f) );
		renderer->ClearDepth(1.0f);

		objMgr->RenderCasterObject(renderer, shader);
		objMgr->RenderReceiverObject(renderer, shader);

		renderer->BindRenderTarget(resPool->Get<IRenderedTexture>("VarPCF2.SMap.1over4"), NULL, NULL, NULL, true);
		renderer->BindTexture(0, resPool->Get<IRenderedTexture>("VarPCF2.SMap") );

		renderer->RenderScreenQuad( resPool->Get<IShader>("VarPCF2.DownSample") );

		renderer->BindTexture(0, NULL );
	}

	//--------------------------------------------------------------------------
	void TVarPCF2::RenderSceneWithShadowMap(
		render::IRenderer *renderer,
		TResourcePool *resPool,
		TObjectManager *objMgr)
	{
		IShader *shader = resPool->Get<IShader>("VarPCF2.Apply");

		renderer->RestoreDefaultRenderTarget();
		
		renderer->BindTexture(3, resPool->Get<IRenderedTexture>("VarPCF2.SMap") );
		renderer->BindTexture(4, resPool->Get<IRenderedTexture>("VarPCF2.SMap.1over4") );

		objMgr->RenderCasterObject(renderer, shader);
		objMgr->RenderReceiverObject(renderer, shader);
		
		renderer->BindTexture(3, NULL);
		renderer->BindTexture(4, NULL);
	}

	//--------------------------------------------------------------------------
}
}
}
